const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers")
const { expect } = require("chai")
const { ethers } = require("hardhat")

describe("Factory", function () {
  // Factory合约的变量
  const FEE = ethers.parseUnits("0.01", 18) // 设置创建代币的费用为0.01 ETH

  // 部署Factory合约的fixture函数
  async function deployFactoryFixture() {
    // 获取账户
    const [deployer, creator, buyer] = await ethers.getSigners()

    // 部署Factory合约
    const Factory = await ethers.getContractFactory("Factory")
    const factory = await Factory.deploy(FEE)

    // 创建代币
    const transaction = await factory.connect(creator).createToken("DAPP Uni", "DAPP", { value: FEE })
    await transaction.wait()

    // 获取代币地址
    const tokenAddress = await factory.tokens(0)
    const token = await ethers.getContractAt("Token", tokenAddress)

    // 返回相关变量
    return { factory, token, deployer, creator, buyer }
  }

  // 购买代币的fixture函数
  async function buyTokenFixture() {
    const { factory, token, creator, buyer } = await deployFactoryFixture()

    const AMOUNT = ethers.parseUnits("10000", 18) // 购买代币的数量
    const COST = ethers.parseUnits("1", 18) // 购买代币的成本

    // 购买代币
    const transaction = await factory.connect(buyer).buyToken(await token.getAddress(), AMOUNT, { value: COST })
    await transaction.wait()

    return { factory, token, creator, buyer }
  }

  // 测试部署部分
  describe("Deployment", function () {
    it("Should set the fee", async function () {
      const { factory } = await loadFixture(deployFactoryFixture)
      expect(await factory.fee()).to.equal(FEE) // 验证费用是否正确设置
    })

    it("Should set the owner", async function () {
      const { factory, deployer } = await loadFixture(deployFactoryFixture)
      expect(await factory.owner()).to.equal(deployer.address) // 验证合约所有者是否正确设置
    })
  })

  // 测试创建代币部分
  describe("Creating", function () {
    it("Should set the owner", async function () {
      const { factory, token } = await loadFixture(deployFactoryFixture)
      expect(await token.owner()).to.equal(await factory.getAddress()) // 验证代币所有者是否正确设置
    })

    it("Should set the creator", async function () {
      const { token, creator } = await loadFixture(deployFactoryFixture)
      expect(await token.creator()).to.equal(creator.address) // 验证代币创建者是否正确设置
    })

    it("Should set the supply", async function () {
      const { factory, token } = await loadFixture(deployFactoryFixture)

      const totalSupply = ethers.parseUnits("1000000", 18) // 总供应量

      expect(await token.balanceOf(await factory.getAddress())).to.equal(totalSupply) // 验证代币供应量是否正确
    })

    it("Should update ETH balance", async function () {
      const { factory } = await loadFixture(deployFactoryFixture)

      const balance = await ethers.provider.getBalance(await factory.getAddress())

      expect(balance).to.equal(FEE) // 验证合约ETH余额是否正确更新
    })

    it("Should create the sale", async function () {
      const { factory, token, creator } = await loadFixture(deployFactoryFixture)

      const count = await factory.totalTokens()
      expect(count).to.equal(1) // 验证代币数量是否正确

      const sale = await factory.checkTokenSale(0)

      expect(sale.token).to.equal(await token.getAddress()) // 验证代币地址是否正确
      expect(sale.creator).to.equal(creator.address) // 验证创建者地址是否正确
      expect(sale.sold).to.equal(0) // 验证已售出代币数量是否正确
      expect(sale.raised).to.equal(0) // 验证已筹集资金是否正确
      expect(sale.isOpen).to.equal(true) // 验证销售是否开启
    })
  })

  // 测试购买代币部分
  describe("Buying", function () {
    const AMOUNT = ethers.parseUnits("10000", 18) // 购买代币的数量
    const COST = ethers.parseUnits("1", 18) // 购买代币的成本

    it("Should update ETH balance", async function () {
      const { factory } = await loadFixture(buyTokenFixture)

      const balance = await ethers.provider.getBalance(await factory.getAddress())

      // 记住初始创建代币的费用加上购买代币的费用
      expect(balance).to.equal(FEE + COST) // 验证ETH余额是否正确更新
    })

    it("Should update token balances", async function () {
      const { token, buyer } = await loadFixture(buyTokenFixture)

      const balance = await token.balanceOf(buyer.address)

      expect(balance).to.equal(AMOUNT) // 验证买家代币余额是否正确更新
    })

    it("Should update token sale", async function () {
      const { factory, token } = await loadFixture(buyTokenFixture)

      const sale = await factory.tokenSales(await token.getAddress())

      expect(sale.sold).to.equal(AMOUNT) // 验证已售出代币数量是否正确更新
      expect(sale.raised).to.equal(COST) // 验证已筹集资金是否正确更新
      expect(sale.isOpen).to.equal(true) // 验证销售是否仍然开启
    })

    it("Should increase base cost", async function () {
      const { factory, token } = await loadFixture(buyTokenFixture)

      const sale = await factory.tokenSales(await token.getAddress())
      const cost = await factory.getCost(sale.sold)

      expect(cost).to.be.equal(ethers.parseUnits("0.0002")) // 验证基础成本是否正确增加
    })
  })

  // 测试存款部分
  describe("Depositing", function () {
    const AMOUNT = ethers.parseUnits("10000", 18) // 购买代币的数量
    const COST = ethers.parseUnits("2", 18) // 购买代币的成本

    it("Sale should be closed and successfully deposits", async function () {
      const { factory, token, creator, buyer } = await loadFixture(buyTokenFixture)

      // 再次购买代币以达到目标
      const buyTx = await factory.connect(buyer).buyToken(await token.getAddress(), AMOUNT, { value: COST })
      await buyTx.wait()

      const sale = await factory.tokenSales(await token.getAddress())
      expect(sale.isOpen).to.equal(false) // 验证销售是否关闭

      const depositTx = await factory.connect(creator).deposit(await token.getAddress())
      await depositTx.wait()

      const balance = await token.balanceOf(creator.address)
      expect(balance).to.equal(ethers.parseUnits("980000", 18)) // 验证创建者代币余额是否正确
    })
  })

  // 测试提取费用部分
  describe("Withdrawing Fees", function () {
    it("Should update ETH balances", async function () {
      const { factory, deployer } = await loadFixture(deployFactoryFixture)

      const transaction = await factory.connect(deployer).withdraw(FEE)
      await transaction.wait()

      const balance = await ethers.provider.getBalance(await factory.getAddress())

      expect(balance).to.equal(0) // 验证合约ETH余额是否正确更新
    })
  })
})